class HD {
	static PENDING =  'pending';
	static FULFILLED =  'fulfilled';
	static REJECTED =  'rejected';


	constructor(executor) {
		this.promiseStatus = HD.PENDING
		this.promiseResut  = null;
		this.callbacks = [];

		try{
			executor(this.resolve.bind(this), this.reject.bind(this))
		} catch(err) {
			this.reject(err)
		}

	}


	resolve(reason) {
		if(this.promiseStatus === HD.PENDING) {
			this.promiseStatus = HD.FULFILLED
			this.promiseResut = reason
			setTimeout(() => {
				this.callbacks.forEach(item => {
					item.onFulfilled(this.promiseResut)
				})
			})
		}
	}
	reject(reason) {
		if(this.promiseStatus === HD.PENDING) {
			this.promiseStatus = HD.REJECTED
			this.promiseResut = reason

			setTimeout(() => {
				this.callbacks.forEach(item => {
					item.onRejected(this.promiseResut)
				})
			})
		}
	}

	then(onFulfilled, onRejected) {
		if(typeof onFulfilled !== 'function') {
			onFulfilled = value => value
		}
		if(typeof onRejected !== 'function') {
			onRejected = reason => reason
		}

		let promise = new HD((resolve, reject) => {
			if(this.promiseStatus === HD.FULFILLED) {
				setTimeout(() => {
					this.parse(promise, onFulfilled, resolve, reject)
				})
			}
			
			if(this.promiseStatus === HD.REJECTED) {
				setTimeout(() => {
					this.parse(promise, onRejected, resolve, reject)
				})
			}


			if(this.promiseStatus === HD.PENDING) {
				this.callbacks.push({
					onFulfilled: value => {
						this.parse(promise, onFulfilled, resolve, reject)
					},
					onRejected: reason => {
						this.parse(promise, onRejected, resolve, reject)
					}
				});
				
			}
		})

		return  promise
	}

	parse(promise, onTypeFn, resolve, reject) {
		let result = onTypeFn(this.promiseResut)
		if(promise === result) {
			throw new TypeError("Chaining cycle detected for promise");
		}
		try{
			if(result instanceof HD) {
				return result.then(onFulfilled, onRejected)
			} else {
	        	resolve(result);
	        }
		} catch(error) {
			(error);
		}
	}

	static resolve(value) {
		return new HD((resolve, reject) => {
			if(value instanceof HD) {
				return value.then(resolve, reject)
			}
			return resolve(value)
		})
	}

	static reject(reason) {
		return new HD((resolve, reject) => {
			return reject(reason)
		}) 
	}

	static all(promises) {
		var resolveRets = []
		return new HD((resolve, reject) => {
			promises.forEach((p, i) => {
				p.then(value => {
					resolveRets[i] = value
					if(resolveRets.length === promises.length) {
						resolve(resolveRets)
					}
				}, reason => {
					reject(reason)
				})
			})
		})
	}

	static race(promises) {
		return new HD((resolve, reject) => {
			promises.forEach((p, i) => {
				p.then(value => {
					resolve(value)
				}, reason => {
					reject(reason)
				})
			})
		})
	}

}